package org.deltaset.meta.faces;

import java.util.logging.Level;
import java.util.logging.Logger;
import org.deltaset.meta.ExecutorResult;
import org.deltaset.meta.Finder;
import org.deltaset.meta.MetaAction;
import org.deltaset.meta.MetaActionExecutor;
import org.deltaset.meta.MetaActionHandler;
import org.deltaset.meta.MetaEntry;
import org.deltaset.meta.MetaHandlerFactory;

/**
 *
 * @author dmakariev
 */
class MetaCrud {

    private static final Logger logger = Logger.getLogger(MetaCrud.class.getName());
    private final MetaHandlerFactory metaHandlerFactory;
    private final FacesStateRepository stateRepository;
    private final MetaActionHandler metaActionHandler;
    private boolean isModelLoaded = false;
    private MetaEntryState metaEntryState;

    public MetaCrud(FacesStateRepository stateRepository, MetaHandlerFactory metaHandlerFactory, MetaActionHandler metaActionHandler) {
        this.stateRepository = stateRepository;
        this.metaHandlerFactory = metaHandlerFactory;
        this.metaActionHandler = metaActionHandler;

        logger.log(Level.INFO, "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ MetaCrud CONSTRUCTOR !!!!!!!!!!!!!!!! ");
    }

    public boolean getHasValidationErrors() {
        return stateRepository.getHasValidationErrors();
    }

    public String getCurrentViewTemplate() {
        if (true == getHasValidationErrors()) {
            previousView();
        }
        final String viewName = getAttributeContext().getViewName();
        if (null == viewName) {
            return null;
        }
        final String currentViewTemplate = stateRepository.getAttributeOrException(viewName);
        return currentViewTemplate;
    }

    private void setCurrentViewName(final String viewName) {
        //this.getMetaEntryState().setCurrentViewName(viewName);
        if (true == getHasValidationErrors()) {
            return;
        }

        final AttributeContext ac = getAttributeContext();
        final AttributeContext newAc = new AttributeContext(ac.getClassName(), viewName, ac.getRecordId());
        getHistoryStack().push(newAc);

    }

    void previousView() {
        //final String lastViewName = getMetaEntryState().getLastViewName();
        final AttributeContext previousAc = getHistoryStack().previous();
        final String previousViewName = previousAc.getViewName();
        logger.log(Level.INFO, "######################### previousViewName={0}", previousViewName);
        this.setCurrentViewName(previousViewName);
    }

    boolean getDisabledPreviousView() {
        return getHistoryStack().hasPrevious();
    }

    private HistoryStack getHistoryStack() {
        return stateRepository.getHistoryStack();
    }

    public void cancelExecutor() {
        getMetaEntryState().setExecutor(null);
        this.previousView();
    }

    public void showDetail() {
        this.setCurrentViewName("detailTemplate");
    }

    public void showList() {
        this.setCurrentViewName("listTemplate");
    }

    public void forceChangeTo(Object item) {
        if (null == item) {
            return;
        }
        final String className = item.getClass().getName();
        final String viewName = "formTemplate";
        final String recordId = getMetaActionHandler().getModelId(item);
        final AttributeContext ac = new AttributeContext(className, viewName, recordId);

        // stateRepository.putAttributeContext(ac);
        getHistoryStack().push(ac);

        refreshState();
    }

    private void refreshState() {
        this.isModelLoaded = false;
        // this.all = null;
        this.metaEntryState = null;

    }

    /////////////////////    
    public Object getModel() {
        final String modelId = getAttributeContext().getRecordId();
        if (null != modelId && false == isModelLoaded) {
            isModelLoaded = true;
            return loadModelInternal(modelId);
        }

        final Object model = this.getMetaEntryState().getModel();
        if (null != model) {
            return model;
        }

        return null;
    }

    public void setModel(final Object model) {
        this.getMetaEntryState().setModel(model);
    }

    public boolean loadModel(final String id) {
        return null != loadModelInternal(id);
    }

////////////////
    public void loadExecutor(final MetaAction metaAction, final Object model) {
        final MetaActionExecutor executor = getMetaHandlerFactory().createMetaActionExecutor(metaAction);
        if (true == metaAction.getHasParameter()) {
            this.setModel(model);
            getMetaEntryState().setExecutor(executor);
        } else {
            this.executeAction(executor, model);
        }
    }

    public void executeAction() {
        final MetaActionExecutor executor = this.getExecutor();
        final Object model = this.getModel();
        executeAction(executor, model);
    }

    public MetaActionExecutor getExecutor() {
        return this.getMetaEntryState().getExecutor();
    }

    public ExecutorResult getExecutorResult() {
        return this.getMetaEntryState().getExecutorResult();
    }
////////////////

    public boolean getShowFinderForm() {
        final MetaActionExecutor finder = getFinder();
        if (null == finder) {
            return false;
        }
        return finder.getMetaAction().getHasParameter();
    }

    public MetaActionExecutor getFinder() {
        return this.getMetaEntryState().getFinder();
    }

    public void cancelFinder() {
        this.getMetaEntryState().setFinder(null);
    }

    public void executeFinder() {
        //TODO: review this method..       
        logger.log(Level.INFO, "executeFinder    ########");
    }

    public void loadFinder(final MetaAction metaAction) {
        final MetaActionExecutor executor = getMetaHandlerFactory().createMetaActionExecutor(metaAction);
        this.getMetaEntryState().setFinder(executor);
    }

    public Finder allFinder(final String pageNum) {
        final MetaActionExecutor finderExecutor = getFinder();
        final MetaAction defaultFinder = this.getMetaEntry().getDefaultFinder();
        final Finder finder = this.getMetaActionHandler().newPageHelper(finderExecutor, defaultFinder);
        return finder;
    }

    MetaEntry getMetaEntry() {
        return getMetaEntryState().getMetaEntry();
    }

//////////////
    private void executeAction(final MetaActionExecutor executor, final Object model) {
        // this.all = null;
        final ExecutorResult executorResult = getMetaActionHandler().executeAction(executor, model);
        getMetaEntryState().setExecutor(null);
        getMetaEntryState().setExecutorResult(executorResult);

        final Object result = executorResult.getValue();
        final MetaAction metaAction = executorResult.getExecutedAction();

        final String methodName = metaAction.getMethodName();
        final String message = "executed '" + methodName + "'";
        addInfoMessage(message);

        if (true == metaAction.getIsReturningEntity()) {
            this.setModel(result);
        }
    }

    private Object loadModelInternal(final String id) {
        final Class entityClass = getMetaEntry().getEntityClass();
        final MetaAction findByIdAction = getMetaEntry().getFindByIdAction();
        final Object entity = getMetaActionHandler().loadModel(entityClass, findByIdAction, id);
        this.setModel(entity);
        return entity;
    }

    private AttributeContext getAttributeContext() {
        final HistoryStack historyStack = getHistoryStack();
        if (historyStack.isEmpty()) {
            final AttributeContext newAttributeContext = initAttributeContext();
            historyStack.push(newAttributeContext);
        }
        if (false == getHasValidationErrors()) {
            final AttributeContext updatedAttributeContext = updatedAttributeContext();
            historyStack.push(updatedAttributeContext);
        }
        return historyStack.current();
    }

    private AttributeContext updatedAttributeContext() {
        final String requestClassName = stateRepository.getRequestEntityClass();
        final String requestViewName = stateRepository.getRequestShowView();
        final String requestRecordId = stateRepository.getRequestRecordId();

        final AttributeContext currentAttributeContext = getHistoryStack().current();
        final String currentClassName = currentAttributeContext.getClassName();
        final String currentViewName = currentAttributeContext.getViewName();
        final String currentRecordId = currentAttributeContext.getRecordId();

        final String className = (null != requestClassName) ? requestClassName : currentClassName;
        final String viewName = (null != requestViewName) ? requestViewName : currentViewName;
        final String recordId = (null != requestRecordId) ? requestRecordId : currentRecordId;

        final AttributeContext ac = new AttributeContext(className, viewName, recordId);
        return ac;
    }

    private AttributeContext initAttributeContext() {
        final String className = stateRepository.getValueEntityClass();
        final String viewName = stateRepository.getAttributeOrException("defaultView");
        final String recordId = null;
        final AttributeContext ac = new AttributeContext(className, viewName, recordId);
        return ac;
    }

    private Class getEntityClassValue() {
        try {
            final String entityClassName = getAttributeContext().getClassName();
            final Class entityClass = Class.forName(entityClassName);
            return entityClass;
        } catch (ClassNotFoundException ex) {
            logger.log(Level.SEVERE, null, ex);
            throw new RuntimeException("class not found", ex);
        }
    }

    private MetaEntryState getMetaEntryState() {
        if (null == metaEntryState) {
            final Class entityClass = getEntityClassValue();
            final MetaHandlerFactory factory = getMetaHandlerFactory();
            metaEntryState = loadMetaEntryState(stateRepository, entityClass, factory);
        }
        return metaEntryState;
    }

    private static MetaEntryState loadMetaEntryState(final FacesStateRepository stateRepository, final Class entityClass, final MetaHandlerFactory factory) {
        final MetaEntryState storedState = stateRepository.getMetaEntryState(entityClass);
        if (null != storedState) {
            return storedState;
        }
        final MetaEntry metaEntry = factory.createMetaEntry(entityClass);
        final MetaEntryState newState = new MetaEntryState(metaEntry);
        stateRepository.putMetaEntryState(entityClass, newState);
        return newState;
    }

    private void addInfoMessage(final String message) {
        stateRepository.addInfoMessage(message);
    }

    private void addErrorMessage(final String message) {
        stateRepository.addErrorMessage(message);
    }

//////////////////////    
    private MetaHandlerFactory getMetaHandlerFactory() {
        return metaHandlerFactory;
    }

    private MetaActionHandler getMetaActionHandler() {
        return metaActionHandler;
    }
}
